home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Snippets / Async Sample / Sample.p < prev    next >
Encoding:
Text File  |  1995-07-11  |  11.6 KB  |  459 lines  |  [TEXT/CWIE]

  1. Program Test;
  2. {---------------------------------------------------------------------------
  3. *
  4. *  This is a sample program which demonstrates async sound.  In this example,
  5. *  three channels are used.  Also added in (per request) is a loop which plays 4 
  6. *  sounds (4 parts of a theme), in order, continuously in one of the channels.
  7. *
  8. *  NOTE: The sound routines are rough and probably can be polished a bit. 
  9. *  Because I am just learning them and taking samples from here and there,
  10. *  there may be useless things/record items declared.  I'll clean all that 
  11. *  up when I get the chance.
  12. *
  13. *  This project uses universal proc pointers, so it can be compiled in either
  14. *  68k mode or PPC mode.  (I also had to make the SndCommand argument a VAR
  15. *  parameter in PlayAsyncCallback to get it to run native).  It works great in 
  16. *  CodeWarrior.  If you want to get the 68K version running successfully using
  17. *  the Think Pascal compiler, you'll have to take out the UPP stuff and replace
  18. *  them with the standard procedure pointers.  You'll also have to remove the
  19. *  toolbox initialization, and probably some of the USES files.
  20. *
  21. *  A window is also setup for displaying what the sound manager is doing during
  22. *  certain parts of execution.  
  23. *
  24. *  Memory:  For this sample, I'm using some rather large sounds, so it is memory
  25. *  intensive.  For 68K, I allocate 2Meg, and for PPC, I allocate 3Meg.  Also,
  26. *  since I utilize the application resource to retrieve the sound (rather than
  27. *  an external sound file which would have been better), you have to pump up the
  28. *  memory for CodeWarrior to successfully link in the sound resources.
  29. *
  30. *  If you have any questions, feel free to contact me at catambay@aol.com.  I'll
  31. *  do my best to answer your questions.
  32. *
  33. *  Regards,
  34. *  Bill Catambay
  35. ---------------------------------------------------------------------------}
  36.  
  37.  
  38. Uses
  39.   Fonts, Types, Segload, Windows, Dialogs, ToolUtils, Resources, QDOffscreen,
  40.   Sound, OSUtils, GestaltEqu;
  41.  
  42. Const
  43.     max_chan = 4;
  44.     
  45. Var
  46.     err: OSerr;
  47.     chan: integer;
  48.     inplay: array[1..max_chan] of boolean;
  49.     the_house: pichandle;
  50.     pics: array[1..3] of pichandle;
  51.     ic:    array[1..8] of pichandle;
  52.     i: integer;
  53.     sndwindow: windowPtr;
  54.     picture: windowPtr;
  55.     say_active: boolean;
  56.     cur_line: integer;
  57.     cur_col: integer;
  58.     use_size: integer;
  59.     font_num: integer;
  60.     theme_song: array[1..4] of handle;
  61.     theme_playing: integer;
  62.     theme_chan: integer;
  63.     outwindow: windowPtr;
  64.     offscreen:    gworldPtr;
  65.     
  66.     {--  SOUND STUFF  --}
  67. Const
  68.     kResourceSoundComplete = 1;
  69.     kHandleSoundComplete = 2;
  70.     AsyncSoundMaxChannels = 4;
  71.  
  72. Type
  73.     ASndQEl = record
  74.         qLink: QElemPtr;
  75.         qType: Integer;
  76.         aQPlaying: Boolean;
  77.         aQCompletionCode: Integer;
  78.         aQSoundHandle: Handle;
  79.         end;
  80.     ASndQElPtr = ^ASndQEl;
  81.     SoundArrayElt = record
  82.         initMode: Longint;
  83.         soundMgrChannel: SndChannelPtr;
  84.         completionQueue: QHdr;
  85.         end;
  86.     SoundArrayHdr = Integer;
  87.     SoundArray = record
  88.         count: SoundArrayHdr;
  89.         channels: array[1..4] of SoundArrayElt;
  90.         end;
  91.     SoundArrayPtr = ^SoundArray;
  92.     SoundArrayHdl = ^SoundArrayPtr;
  93.  
  94. Var
  95.     theAsyncChannels: SoundArrayHdl;
  96.  
  97. Function AsyncSoundInit (numberOfChannels: Integer): OSErr;
  98.  
  99. Procedure Check (result: Integer);
  100.  
  101.     begin
  102.     AsyncSoundInit := result;
  103.     if result <> noErr then
  104.         begin
  105.         theAsyncChannels := nil;
  106.         Exit(AsyncSoundInit);
  107.         end;
  108.     end;
  109.  
  110. Var
  111.     gestaltResult: Longint;
  112.     i: Integer;
  113.  
  114.     begin
  115.     Check(Gestalt(gestaltSoundAttr, gestaltResult));
  116.     if (numberOfChannels <= 0) or (numberOfChannels > AsyncSoundMaxChannels) then
  117.         Check(paramErr);
  118.     theAsyncChannels := SoundArrayHdl(NewHandleClear(SIZEOF(SoundArrayHdr) + 
  119.         SIZEOF(SoundArrayElt) * numberOfChannels));
  120.     if theAsyncChannels <> nil then
  121.         with theAsyncChannels^^ do
  122.             begin
  123.             count := numberOfChannels;
  124.             for i := 1 to count do
  125.                 channels[i].initMode := initMono;
  126.             end;
  127.     Check(MemError);
  128.     end;
  129.  
  130. Procedure PopQueue (whichChannel: Integer);
  131.  
  132. Var
  133.     poppedQElem: Ptr;
  134.     myErr: OSErr;
  135.  
  136.     begin
  137.     with theAsyncChannels^^.channels[whichChannel], completionQueue do
  138.         begin
  139.         poppedQElem := Ptr(qHead);
  140.         myErr := Dequeue(qHead, @completionQueue);
  141.         DisposePtr(poppedQElem);
  142.         end;
  143.     end;
  144.  
  145. Procedure AsyncSoundIdle;
  146.  
  147. Var
  148.     whichChannel: Integer;
  149.     firstQElt: aSndQElPtr;
  150.     myErr: OSErr;
  151.  
  152.     begin
  153.     if theAsyncChannels <> nil then
  154.         begin
  155.         HLock(Handle(theAsyncChannels));
  156.         with theAsyncChannels^^ do
  157.             for whichChannel := 1 to count do
  158.                 with channels[whichChannel] do
  159.                     begin
  160.                     if completionQueue.qHead <> nil then
  161.                         repeat
  162.                             firstQElt := aSndQElPtr(completionQueue.qHead);
  163.                             with firstQElt^ do
  164.                                 if not aQPlaying then
  165.                                     begin
  166.                                     if aQCompletionCode = kResourceSoundComplete then
  167.                                         DisposeHandle(aQSoundHandle);
  168.                                     PopQueue(whichChannel);
  169.                                     end;
  170.                         until (completionQueue.qHead = nil) | firstQElt^.aQPlaying;
  171.                     if completionQueue.qHead = nil then
  172.                         begin
  173.                         myErr := SndDisposeChannel(soundMgrChannel, True);
  174.                         soundMgrChannel := nil;
  175.                         end;
  176.                     end;
  177.         HUnlock(Handle(theAsyncChannels));
  178.         end;
  179.     end;
  180.  
  181. Function FindFreeAsyncSoundChannel: Integer;
  182.  
  183. Var
  184.     whichChannel: Integer;
  185.  
  186.     begin
  187.     FindFreeAsyncSoundChannel := 0;
  188.     if theAsyncChannels <> nil then
  189.         with theAsyncChannels^^ do
  190.             for whichChannel := 1 to count do
  191.                 with channels[whichChannel], completionQueue do
  192.                     if qHead = nil then
  193.                         begin
  194.                         FindFreeAsyncSoundChannel := whichChannel;
  195.                         Leave;
  196.                         end;
  197.     end;
  198.     
  199. Procedure PlayAsyncCallback (chan: SndChannelPtr; Var cmd: SndCommand);
  200.  
  201.     begin
  202.     case cmd.param1 of
  203.         kResourceSoundComplete, 
  204.         kHandleSoundComplete:    ASndQElPtr(cmd.param2)^.aQPlaying := False;
  205.         otherwise                ;
  206.     {CASE}    end;
  207.     end;
  208.  
  209. Function PlayAsyncSound (soundHandle: Handle; 
  210.                          whichChannel: Integer; 
  211.                          completionCode: Integer): OSErr;
  212.  
  213. Procedure Check (result: OSErr);
  214.  
  215.     begin
  216.     PlayAsyncSound := result;
  217.     if result <> noErr then
  218.         Exit(PlayAsyncSound);
  219.     end;
  220.  
  221. Var
  222.     mySndChan: SndChannelPtr;
  223.     mySndCmd: SndCommand;
  224.     sndUPP: SndCallBackUPP;
  225.  
  226.     begin
  227.     if (whichChannel <= 0) or (whichChannel > theAsyncChannels^^.count) then
  228.         Check(paramErr);
  229.     HLock(Handle(theAsyncChannels));
  230.     with theAsyncChannels^^.channels[whichChannel] do
  231.         if completionQueue.qHead = nil then
  232.             begin
  233.             mySndChan := nil;
  234.             sndUPP := NewSndCallBackProc(@PlayAsyncCallback);
  235.             Check(SndNewChannel(mySndChan, sampledSynth, initMode, sndUPP));
  236.             end
  237.         else
  238.             mySndChan := soundMgrChannel;
  239.     Check(SndPlay(mySndChan, SndListHandle(soundHandle), True));
  240.     with mySndCmd do
  241.         begin
  242.         cmd := callBackCmd;
  243.         param1 := completionCode;
  244.         param2 := Longint(NewPtr(SIZEOF(ASndQEl)));
  245.         with ASndQElPtr(param2)^ do
  246.             begin
  247.             aQPlaying := True;
  248.             aQCompletionCode := completionCode;
  249.             aQSoundHandle := soundHandle;
  250.             end;
  251.         with theAsyncChannels^^.channels[whichChannel] do
  252.             begin
  253.             soundMgrChannel := mySndChan;
  254.             Enqueue(QElemPtr(param2), @completionQueue);
  255.             end;
  256.         end;
  257.     Check(SndDoCommand(mySndChan, mySndCmd, False));
  258.     end;
  259.  
  260. Function PlayAsyncSoundResource (soundID: Integer; 
  261.                                  whichChannel: Integer): OSErr;
  262.  
  263. Var
  264.     soundHandle: Handle;
  265.  
  266.     begin
  267.     if theAsyncChannels <> nil then
  268.         begin
  269.         soundHandle := GetResource('snd ', soundID);
  270.         if soundHandle <> nil then
  271.             begin
  272.             DetachResource(soundHandle);
  273.             PlayAsyncSoundResource := PlayAsyncSound(soundHandle, whichChannel, 
  274.                 kResourceSoundComplete);
  275.             end
  276.         else
  277.             PlayAsyncSoundResource := ResError;
  278.         end
  279.     else
  280.         begin  { not really sure what to do in this case }
  281.         PlayAsyncSoundResource := noErr;
  282.         end;
  283.     end;
  284.  
  285. Function AsyncSoundChannelActive (whichChannel: Integer): Boolean;
  286.  
  287.     begin
  288.     if theAsyncChannels <> nil then
  289.         begin
  290.         if (whichChannel > 0) and (whichChannel <= theAsyncChannels^^.count) then
  291.             AsyncSoundChannelActive := 
  292.                 theAsyncChannels^^.channels[whichChannel].completionQueue.qHead <> nil
  293.         else
  294.             AsyncSoundChannelActive := False;
  295.         end
  296.     else
  297.         AsyncSoundChannelActive := False;
  298.     end;
  299.  
  300. Procedure say(txt:    str255);
  301.  
  302.     begin
  303.     if not say_active then
  304.         exit(say);
  305.     setport(outwindow);
  306.     selectwindow(outwindow);
  307.     use_size := 9;
  308.     textsize(use_size);
  309.     textfont(font_num);
  310.     cur_line := cur_line + use_size + 2;
  311.     if cur_line > 1000 then
  312.         begin
  313.         cur_line := 1;
  314.         cur_col := cur_col + use_size*40;
  315.         if cur_col > 1000 then
  316.             cur_col := 10;
  317.         end;
  318.     if cur_line > outwindow^.portRect.bottom then
  319.         begin
  320.         EraseRect(outwindow^.portRect);
  321.         cur_line := 2 + use_size;
  322.         end;
  323.     MoveTo(cur_col,cur_line);
  324.     drawstring(txt);
  325.     setport(FrontWindow);
  326.     end; { of say }
  327.     
  328. Procedure Initialize_debug;
  329.  
  330. Var
  331.     outrect:    rect;
  332.     
  333.     begin
  334.     getfnum('Monaco',font_num);
  335.     outrect.top := 60;
  336.     outrect.left := 10;
  337.     outrect.right := outrect.left + 400;
  338.     outrect.bottom := outrect.top + 400;
  339.     outwindow := NewCwindow(NIL, outrect, 'Output Window', TRUE, documentProc,
  340.         Pointer(-1),TRUE,0);
  341.     cur_line := 10;
  342.     cur_col := 10;
  343.     end;
  344.     
  345. Function num2str(num:    integer): string;
  346.  
  347. Var
  348.     dig:        integer;
  349.     str,sign:    str255;
  350.     
  351.     begin
  352.     numTostring(num,str);
  353.     num2str := str;
  354.     end; { of num2str }
  355.         
  356. Procedure Toolbox_init;
  357.  
  358.     begin
  359.     say_active := TRUE;
  360.     initGraf(@qd.thePort);
  361.     initFonts;
  362.     initWindows;
  363.     initMenus;
  364.     TEinit;
  365.     initDialogs(nil);
  366.     MaxApplZone;
  367.     InitCursor;
  368.     end;
  369.  
  370. Procedure Play_theme;
  371.  
  372. var
  373.     next_theme:    integer;
  374.     err: OSerr;
  375.     
  376.     begin
  377.     if theme_playing = 0 then
  378.         begin
  379.         say('Initializing theme...');
  380.         theme_playing := 1;
  381.         theme_chan := FindFreeAsyncSoundChannel;
  382.         theme_song[1] := GetResource('snd ',151);
  383.         next_theme := 2;
  384.         end
  385.     else
  386.         begin
  387.         disposeHandle(theme_song[theme_playing]);
  388.         theme_playing := theme_playing + 1;
  389.         if theme_playing > 4 then
  390.             theme_playing := 1;
  391.         next_theme := theme_playing + 1;
  392.         if next_theme > 4 then
  393.             next_theme := 1;
  394.         end;
  395.     say(concat('Playing part ',num2str(theme_playing),' of theme...'));
  396.     DetachResource(theme_song[theme_playing]);
  397.     err := PlayAsyncSound(theme_song[theme_playing], theme_chan, 
  398.         kResourceSoundComplete);
  399.     say(concat('Play theme status: ',num2str(err)));
  400.     theme_song[next_theme] := GetResource('snd ',150 + next_theme);
  401.     end;
  402.  
  403. begin
  404. toolbox_init;
  405. if say_active then
  406.     initialize_debug;
  407. say('Setting up sound...');
  408. err := AsyncSoundInit(max_chan);
  409. say(concat('Initializing status: ',num2str(err)));
  410. chan := FindFreeAsyncSoundChannel;
  411. say(concat('Found free channel ',num2str(chan),' for resource 131'));
  412. err := PlayAsyncSoundResource (131, chan);
  413. say(concat('Got resource 131 status: ',num2str(err)));
  414. chan := FindFreeAsyncSoundChannel;
  415. say(concat('Found free channel ',num2str(chan),' for resource 132'));
  416. err := PlayAsyncSoundResource (132, chan);
  417. say(concat('Got resource 132 status: ',num2str(err)));
  418. for i := 1 to max_chan do
  419.     inplay[i] := false;
  420. {----------------------------------------------------------------}
  421. {  Loop until all sound is completed, or until button is pressed }
  422. {----------------------------------------------------------------}
  423. repeat
  424.     AsyncSoundIdle;
  425.     for i := 1 to max_chan do
  426.         if AsyncSoundChannelActive(i) and (not inplay[i]) then
  427.             begin
  428.             inplay[i] := true;
  429.             say(concat('Playing channel ',num2str(i)));
  430.             end
  431.         else if (not AsyncSoundChannelActive(i)) and inplay[i] then
  432.             begin
  433.             inplay[i] := false;
  434.             say(concat('Done with channel ',num2str(i)));
  435.             end;
  436.     if not AsyncSoundChannelActive(theme_chan) then
  437.         begin
  438.         say('Calling Play_theme procedure...');
  439.         play_theme;
  440.         end;
  441.     {----------------------------------------------------------------}
  442.     { NOTE: Code below is meant to exit loop when all sound is done. }
  443.     { However, since above code was added, sound will never end; so  }
  444.     { loop is only exited upon pressing the mouse button.            }
  445.     {----------------------------------------------------------------} 
  446.     for i := 1 to max_chan do
  447.         if AsyncSoundChannelActive(i) then
  448.             leave;  { found active channel -> exit for loop }
  449.     if i > Max_chan then
  450.         begin
  451.         say('Finished!');
  452.         leave;  { all channels done -> exit repeat loop }
  453.         end;
  454. until button;
  455. repeat until not button;
  456. say('Press mouse button');  
  457. repeat until button;
  458. end.
  459.